﻿namespace MsForgeReboot.Services {
    #region Usings

    using System;
    using System.Linq;


    using MsForgeReboot.Framework.Entities;
    using MsForgeReboot.Framework.Interfaces;

    using PagedList;

    #endregion

    /// <summary>
    ///     Encapsulates operations with blog storage
    /// </summary>
    public class BlogService : IBlogService {
        #region Constants and Fields

        private readonly MsForgeContext context;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="BlogService" /> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public BlogService(MsForgeContext context) {
            this.context = context;
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        ///     Deletes the blogpost by specified id.
        /// </summary>
        /// <param name="urlPointer"></param>
        public void Delete(string urlPointer) {
            this.context.BlogPosts.Remove(this.context.BlogPosts.Single(x => x.UrlPointer == urlPointer));
            this.context.SaveChanges();
        }

        /// <summary>
        ///     Gets the tag.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public Tag GetTag(string tag) {
            return this.context.Tags.Single(t => t.Name == tag);
        }

        /// <summary>
        ///     Gets the tag with blog posts.
        /// </summary>
        /// <param name="tagName"></param>
        /// <param name="page">The page number.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <returns></returns>
        public Tag GetTagWithBlogPosts(string tagName, int page, int pageSize) {
            var tag = this.context.Tags.Single(t => t.Name == tagName);
            return tag;
        }

        public IPagedList<BlogPost> GetListByTag(string tagName, int page, int pageSize) {
            var result = from bp in this.context.BlogPosts
                         where bp.Tags.Any(t => t.Name == tagName)
                         orderby bp.Published descending
                         select bp;

            return new PagedList<BlogPost>(result, page, pageSize);
        }

        /// <summary>
        ///     Saves the comment.
        /// </summary>
        /// <param name="comment">The comment.</param>
        public void SaveComment(Comment comment) {
            this.context.Comments.Add(comment);
            this.context.SaveChanges();
        }

        /// <summary>
        ///     Gets the BlogPost by title.
        /// </summary>
        /// <param name="urlPointer"></param>
        /// <returns></returns>
        public BlogPost GetByUrlPointer(string urlPointer) {
            var result = this.context.BlogPosts.FirstOrDefault(x => x.UrlPointer == urlPointer);

            if (result != null) {
                result.Comments.OrderByDescending(c => c.CommentedOn).ToList();
            }
            return result;
        }

        /// <summary>
        ///     Gets the paged list of blog posts.
        /// </summary>
        /// <param name="pageNumber"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public IPagedList<BlogPost> GetList(int pageNumber, int pageSize) {
            return this.context.BlogPosts.OrderByDescending(x => x.Published)
                       .Select(x => x)
                       .ToPagedList(pageNumber, pageSize);
        }

        /// <summary>
        ///     Saves the instance of <see cref="BlogPost" /> to the repository.
        /// </summary>
        /// <param name="blogPost"> The blog post. </param>
        public void Save(BlogPost blogPost) {
            blogPost.Author = this.context.Users.Single(user => user.Id == blogPost.Author.Id);
            this.MakeSureUrlTitleIsUnique(blogPost);

            if (blogPost.Id == 0) {
                this.InsertBlogPost(blogPost);
            }
            else {
                this.UpdateBlogPost(blogPost);
            }

            this.context.SaveChanges();
        }

        #endregion

        #region Methods

        /// <summary>
        ///     Inserts the blog post.
        /// </summary>
        /// <param name="blogPost"> The blog post. </param>
        private void InsertBlogPost(BlogPost blogPost) {
            this.context.BlogPosts.Add(blogPost);
        }

        /// <summary>
        ///     Makes the sure URL title is unique.
        /// </summary>
        /// <param name="blogPost"> The blog post. </param>
        private void MakeSureUrlTitleIsUnique(Post blogPost) {
            var i = 1;

            var existingBlogPost = this.GetByUrlPointer(blogPost.UrlPointer);

            while (existingBlogPost != null) {
                if (blogPost.Id != existingBlogPost.Id) {
                    blogPost.UrlPointer = blogPost.UrlPointer + i;
                    existingBlogPost = this.GetByUrlPointer(blogPost.UrlPointer);
                    i++;
                }
            }
        }

        /// <summary>
        ///     Updates the blog post.
        /// </summary>
        /// <param name="blogPost"> The blog post. </param>
        private void UpdateBlogPost(BlogPost blogPost) {
            var originalBlogPost = this.context.BlogPosts.Single(x => x.Id == blogPost.Id);

            originalBlogPost.Author = blogPost.Author;
            originalBlogPost.Body = blogPost.Body;
            originalBlogPost.Published = DateTime.Now;
            originalBlogPost.Tags = blogPost.Tags;
            originalBlogPost.Title = blogPost.Title;
            originalBlogPost.UrlPointer = blogPost.UrlPointer;
        }

        #endregion
    }
}